import java.util.LinkedList;

public class checkType {

//	String _value;
//	String _type;
	String currentFuncName;
	String returnType;
	
	public LinkedList<String> paramList;
	
	public LinkedList<typeTable> tt;
	
	checkType() {
		
		this.returnType = "";
		this.paramList = new LinkedList<String>(); 
		
		tt = new LinkedList<typeTable>();
		tt.push(new typeTable("int", "numPlayers"));
		tt.push(new typeTable("int", "numTurns"));
		tt.push(new typeTable("int", "numRounds"));
		tt.push(new typeTable("int", "currentPlayer"));
		tt.push(new typeTable("boolean", "activePlayer"));
		tt.push(new typeTable("boolean", "running"));
		tt.push(new typeTable("String", "title"));
		tt.push(new typeTable("int", "width"));
		tt.push(new typeTable("int", "height"));
		tt.push(new typeTable("Player", "player"));
		tt.push(new typeTable("Dice", "dice"));
		tt.push(new typeTable("int", "points"));
		tt.push(new typeTable("String", "name"));
		tt.push(new typeTable("boolean", "direction"));
		tt.push(new typeTable("Cell", "startLocation"));
		tt.push(new typeTable("int", "delay"));
		tt.push(new typeTable("int", "sides"));
	}
	
	void setCurrentFuncName(String name) {
		
		this.currentFuncName = name;
		
//		System.out.println("--Set Current Func Name--");
//		System.out.println("Current: " + name);
	}
	
	String getCurrentFuncName() {
		
		return this.currentFuncName;
	}
	
	void setReturnType(BGDLTreeNode returnType) {
		
		this.returnType = returnType.value;
	}
	
	// set ID type to node 
	String checkIDType(BGDLTreeNode type) {
		
		if (type.value.equals("String")) {
			return "String";
		}
		else if (type.value.equals("int")) {
			return "int";
		}
		else if (type.value.equals("double")) {
			return "double";
		}
		else if (type.value.equals("boolean")) {
			return "boolean";
		}
		else if (type.value.equals("Board")) {
			return "Board";
		}
		else if (type.value.equals("Player")) {
			return "Player";
		}
		else if (type.value.equals("Dice")) {
			return "Dice";
		}
		else if (type.value.equals("Cell")) {
			return "Cell";
		}
		
		return "";
	}
	
	// set ID type to node 
	String checkIDType(String type) {
		
		if (type.equals("String")) {
			return "String";
		}
		else if (type.equals("int")) {
			return "int";
		}
		else if (type.equals("double")) {
			return "double";
		}
		else if (type.equals("boolean")) {
			return "boolean";
		}
		else if (type.equals("Board")) {
			return "Board";
		}
		else if (type.equals("Player")) {
			return "Player";
		}
		else if (type.equals("Dice")) {
			return "Dice";
		}
		else if (type.equals("Cell")) {
			return "Cell";
		}
		
		return "";
	}
	
	// 	 Checking
	boolean compareAstParam(BGDLTreeNode node, BGDLTreeNode prop) {
		
		
		if (node.value.equals("Board")) {
			if (prop.value.equals("title") || prop.value.equals("width") || prop.value.equals("height")
				|| prop.value.equals("player") || prop.value.equals("dice"))
				return true;
		}
		else if (node.value.equals("GameState")) {
			if (prop.value.equals("numPlayers") || prop.value.equals("numTurns") || prop.value.equals("numRounds")
				|| prop.value.equals("currentPlayer") || prop.value.equals("activePlayer") || prop.value.equals("running"))
				return true;
		}
		else if (node.value.equals("Player")) {
			if (prop.value.equals("points") || prop.value.equals("name") || prop.value.equals("direction")
				|| prop.value.equals("startLocation") || prop.value.equals("delay"))
				return true;
		}
		else if (node.value.equals("Dice")) {
			if (prop.value.equals("sides"))
				return true;
		}
		else if (node.value.equals("array_expr")) {
//			System.out.println("child 0-1: " + node.children.get(0).children.get(1).value);
			if (node.children.get(0).children.get(1).value.equals("Board")) {
				if (prop.value.equals("title") || prop.value.equals("width") || prop.value.equals("height")
					|| prop.value.equals("player") || prop.value.equals("dice"))
					return true;
			}
			else if (node.children.get(0).children.get(1).value.equals("GameState")) {
				if (prop.value.equals("numPlayers") || prop.value.equals("numTurns") || prop.value.equals("numRounds")
					|| prop.value.equals("currentPlayer") || prop.value.equals("activePlayer") || prop.value.equals("running"))
					return true;
			}
			else if (node.children.get(0).children.get(1).value.equals("player")) {
				if (prop.value.equals("points") || prop.value.equals("name") || prop.value.equals("direction")
					|| prop.value.equals("startLocation") || prop.value.equals("delay"))
					return true;
			}
			else if (node.children.get(0).children.get(1).value.equals("dice")) {
				if (prop.value.equals("sides"))
					return true;
			}
			
		}
		
		System.out.println("Abstract Type misMatch");
		System.out.println("Type: " + node.value + ", Property: " + prop.value);
		
		return false;
	}
	
	// dot_expr ASSIGN assign_expr
	boolean compareDotAssignParam(BGDLTreeNode node1, BGDLTreeNode node2) {
		
		String value = node1.children.get(1).value;
		String type;
		
//		System.out.println("Start CompareDotAssignParam");
//		System.out.println("node2.value: " + node2.value);
		if (node2.children.size() > 1) {
//			System.out.println("node2.children.get(0).type: " + node2.children.get(0).type);
			type = node2.children.get(0).type;
		}
		else {
			type = idToType(node2.value);
//			System.out.println("node2.value: " + type);
		}
		
//		System.out.println("type: " + type);
		
		if (type.equals("") && node2.children.size() > 0) {
			type = node2.children.get(0).type;
		}
		
		String complexType = "";
		
		// check complex type
		if (node2.children.size() > 1) {
			complexType = node2.children.get(0).value;
			
			if (value.equals("dice") && complexType.equals("Dice")) {
				return true;
			}
			else if (value.equals("player") && type.equals("Player")) {
				return true;
			}
		}
//		System.out.println("value : " + value);
//		System.out.println("type : " + type);
//		System.out.println("complexType : " + complexType);
		
		
		// GameState
		if (value.equals("numPlayers") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("numTurns") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("numRounds") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("currentPlayer") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("running") && (type.equals("boolean") || type.equals("BOOLEAN_LITERAL"))) {
			return true;
		}
		// Board 
		if (value.equals("title") && (type.equals("String") || type.equals("STRING_LITERAL"))) {
			return true;
		}
		else if (value.equals("width") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("height") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		// Player
		if (value.equals("points") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		else if (value.equals("name") && (type.equals("String") || type.equals("STRING_LITERAL"))) {
			return true;
		}
		else if (value.equals("direction") && (type.equals("boolean") || type.equals("BOOLEAN_LITERAL"))) {
			return true;
		}
		else if (value.equals("delay") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		// Dice
		if (value.equals("sides") && (type.equals("int") || type.equals("INT_LITERAL"))) {
			return true;
		}
		
		System.out.println("Property does not match with abstract type");
		System.out.println("Property: " + node1.children.get(1).value + ", Type: " + type);
		// System.out.println("Property: " + node1.children.get(1).value + ", Type: " + node2.children.get(0).type);
		
		return false;
	}
	
	boolean compareIDAssignParam(String id, BGDLTreeNode node) {
		
		System.out.println("--compareIDAssignParam--");
		System.out.println("ID: " + id);
		System.out.println("node  : " + node.value);
		
		
		if (id.equals("int") && node.value.equals("INT_LITERAL"))
			return true;
		else if (id.equals("double") && node.value.equals("DOUBLE_LITERAL"))
			return true;
		else if (id.equals("String") && node.value.equals("STRING_LITERAL"))
			return true;
		else if (id.equals("boolean") && node.value.equals("BOOLEAN_LITERAL"))
			return true;
		
		System.out.println("Assignment type mismatch");
		System.out.println("ID: " + id + ", Type: " + node.value);
		
		return false;
	}
	
	boolean compareIDAssignParam(BGDLTreeNode varInit, BGDLTreeNode node) {
		
//		System.out.println("--compareInitIDAssignParam--");
//		System.out.println("ID: " + varInit.children.get(0).value);
//		System.out.println("node  : " + node.value);
		
		String id = varInit.children.get(0).value;
		
		if (id.equals("int") && node.value.equals("INT_LITERAL"))
			return true;
		else if (id.equals("int") && node.value.equals("unary -"))
			return true;
		else if (id.equals("double") && node.value.equals("DOUBLE_LITERAL"))
			return true;
		else if (id.equals("String") && node.value.equals("STRING_LITERAL"))
			return true;
		else if (id.equals("boolean") && node.value.equals("BOOLEAN_LITERAL"))
			return true;
		
		System.out.println("Assignment type mismatch");
		System.out.println("ID: " + id + ", Type: " + node.value);
		
		return false;
	}

	boolean compareComplexAssignParam(BGDLTreeNode varInit, BGDLTreeNode node) {
		
//		System.out.println("--compareInitIDAssignParam--");
//		System.out.println("ID: " + varInit.children.get(0).value);
//		System.out.println("node  : " + node.value);
		
		String id = varInit.children.get(0).value;
		String type = node.children.get(2).value;
		
		if (id.equals("Player") && type.equals("Player"))
			return true;
		else if (id.equals("Dice") && type.equals("Dice"))
			return true;
		
		System.out.println("Complex Type assignment mismatch");
		System.out.println("ID: " + id + ", Type: " + node.value);
		
		return false;
	}
	
	
	boolean compareORParam(String left, BGDLTreeNode right, String param) {
		
//		System.out.println("--compareORAssignParam--");
//		System.out.println("ID: " + left);
//		System.out.println("node  : " + right.value);
//		System.out.println("child size   : " + right.children.size());
//		System.out.println("child 1   : " + right.children.get(0).value);
//		if (right.children.size() > 1)
//			System.out.println("child 2   : " + right.children.get(1).value);
		
		if (left.equals("int") && right.value.equals("INT_LITERAL"))
			return true;
		else if (left.equals("double") && right.value.equals("DOUBLE_LITERAL"))
			return true;
		else if (left.equals("String") && right.value.equals("STRING_LITERAL"))
			return true;
		else if (left.equals("boolean") && right.value.equals("BOOLEAN_LITERAL"))
			return true;
		else if (right.value.equals(".")) {
			if (left.equals("int") && idToType(right.children.get(1).value).equals("int"))
				return true;
			else if (left.equals("double") && idToType(right.children.get(1).value).equals("double"))
				return true;
			else if (left.equals("String") && idToType(right.children.get(1).value).equals("String"))
				return true;
			else if (left.equals("boolean") && idToType(right.children.get(1).value).equals("boolean"))
				return true;
		}
		
		
		
		System.out.println("Compare type mismatch: OR Param( " + param + " )");
		System.out.println("Left: " + left + ", Right: " + right.value);
		
		return false;
	}
	
	boolean checkFunctionType(BGDLTreeNode returnType, String funcID) {
		
		//System.out.println("returnType: " + returnType.value);
		
		if (returnType.value.equals("void") && funcID.equals("InitializeBoard"))
			return true;
		else if (returnType.value.equals("void") && funcID.equals("InitializeCells"))
			return true;
		else if (returnType.value.equals("boolean") && funcID.equals("GameOver"))
			return true;
		else if (returnType.value.equals("Player") && funcID.equals("Winner"))
			return true;
			
		System.out.println("Function return type does not match");
		System.out.println("Function name : " + funcID + ", return type: " + returnType.value);
		
		return false;
	}
	
	boolean checkArrayAssignType(BGDLTreeNode value, BGDLTreeNode type) {
		
		//System.out.println("value: " + value.children.get(0).value + " type: " + type.children.get(1).value);
		String left = value.children.get(0).value;
		String right = type.children.get(1).value;
		
		if (left.equals("int") && right.equals("INT_LITERAL"))
			return true;
		else if (left.equals("double") && right.equals("DOUBLE_LITERAL"))
			return true;
		else if (left.equals("String") && right.equals("STRING_LITERAL"))
			return true;
		else if (left.equals("boolean") && right.equals("BOOLEAN_LITERAL"))
			return true;
		
		
		System.out.println("Array type mismatch");
		System.out.println("Left: " + left + ", Right: " + right);
		
		return false;
	}
	
	boolean compareReturnType() {
		
//		System.out.println("--Compare Return Type--");
//		System.out.println("currFunc: " + this.currentFuncName + ", type: " + idToType(this.returnType));
		
		if (this.currentFuncName.equals("InitializeBoard") && this.returnType.equals("")) {
			this.returnType = "";
			return true;
		}
		else if (this.currentFuncName.equals("GameOver") && this.returnType.equals("BOOLEAN_LITERAL")) {
			this.returnType = "";
			return true;
		}
		else if (this.currentFuncName.equals("Winner") && idToType(this.returnType).equals("Player")) {
			this.returnType = "";
			return true;
		}
		else if (this.currentFuncName.equals("InitializeCells") && this.returnType.equals("")) {
			this.returnType = "";
			return true;
		}
		
		System.out.println("Function Return Type Mismatch");
		System.out.println("currFunc: " + this.currentFuncName + ", Return type: " + idToType(this.returnType));
		
		return false;
	}
	
	void saveFuncParam(BGDLTreeNode node) {
		
//		System.out.println("func param1: " + node.value);
		String value = node.value;
		
		if (value.equals("INT_LITERAL") || value.equals("BOOLEAN_LITERAL") || value.equals("STRING_LITERAL")
				|| value.equals("cell_expr")) {
			this.paramList.push(value);
		}
		else if (value.equals(".")){
//			System.out.println("Dot: " + node.children.get(1).value);
			value = node.children.get(1).value;
			this.paramList.push(idToType(node.children.get(1).value));
		}
		else {
			this.paramList.push(idToType(value));
		}
		
	}
	
	void printParamList() {
		
		for (int i = 0; i <this.paramList.size(); i++) {
			System.out.println("param" + i + ": " + this.paramList.get(i));
		}
	}
	
	boolean compareParamList(BGDLTreeNode complexType) {
		
		if (this.paramList.size() == 5) {
//			System.out.println("5");
//			printParamList();
			if ((this.paramList.get(0).equals("INT_LITERAL")||this.paramList.get(0).equals("int")) 
					&& (this.paramList.get(1).equals("STRING_LITERAL")||this.paramList.get(1).equals("String"))
					&& (this.paramList.get(2).equals("BOOLEAN_LITERAL")||this.paramList.get(2).equals("boolean")) 
					&& this.paramList.get(3).equals("cell_expr")
					&& (this.paramList.get(4).equals("INT_LITERAL")||this.paramList.get(4).equals("int"))) {
				this.paramList.clear();
				
				return true;
			}
		}
		else if (this.paramList.size() == 4) {
//			System.out.println("4");
//			printParamList();
			if ((this.paramList.get(0).equals("INT_LITERAL")||this.paramList.get(0).equals("int")) 
					&& (this.paramList.get(1).equals("STRING_LITERAL")||this.paramList.get(1).equals("String"))
					&& (this.paramList.get(2).equals("BOOLEAN_LITERAL")||this.paramList.get(2).equals("boolean"))
					&& this.paramList.get(3).equals("cell_expr")) {
				this.paramList.clear();
				
				return true;
			}
		}
		else if (this.paramList.size() == 3) {
//			System.out.println("3");
//			printParamList();
			if ((this.paramList.get(0).equals("INT_LITERAL")||this.paramList.get(0).equals("int")) 
					&& (this.paramList.get(1).equals("STRING_LITERAL")||this.paramList.get(1).equals("String"))
					&& (this.paramList.get(2).equals("BOOLEAN_LITERAL")||this.paramList.get(2).equals("boolean"))) {
				this.paramList.clear();
				
				return true;
			}
		}
		else if (this.paramList.size() == 2) {
//			System.out.println("2");
//			printParamList();
			if ((this.paramList.get(0).equals("INT_LITERAL")||this.paramList.get(0).equals("int")) 
					&& (this.paramList.get(1).equals("STRING_LITERAL")||this.paramList.get(1).equals("String"))) {
				this.paramList.clear();
				
				return true;
			}
		}
		else if (this.paramList.size() == 1) {
//			System.out.println("1");
//			printParamList();
			if ((this.paramList.get(0).equals("INT_LITERAL")||this.paramList.get(0).equals("int"))) {
				this.paramList.clear();
				
				return true;
			}
		}
		
		System.out.println("Function Parameter Type Mismatch");
//		System.out.println("Unproper Parameter Number or Type");
//		printParamList();
//		System.out.println("currFunc: " + this.currentFuncName + ", Return type: " + idToType(this.returnType));
		
		return false;
	}
	
	boolean compareCellParam(BGDLTreeNode param1, BGDLTreeNode param2) {
		
//		System.out.println("Cell param1: " + param1.value);
//		System.out.println("Cell param2: " + param2.value);
		
		if (param1.value.equals("INT_LITERAL") && param2.value.equals("INT_LITERAL")) {
			return true;
		}
		else if (param1.value.equals("INT_LITERAL") && idToType(param2.value).equals("int")) {
			return true;
		}
		else if (idToType(param1.value).equals("int") && param2.value.equals("INT_LITERAL")) {
			return true;
		}
		else if (idToType(param1.value).equals("int") && idToType(param2.value).equals("int")) {
			return true;
		}
		
		System.out.println("Cell parameter mismatch");
		System.out.println("Param1: " + param1.value + " Param2: " + param2.value);
		
		return false;
	}
	
	boolean compareActionParam(String actionID, BGDLTreeNode param) {
//		System.out.println("Action ID: " + actionID);
//		System.out.println("Action param1: " + param.children.get(0).children.get(0).children.get(1).value);
//		System.out.println("Action param2: " + param.children.get(1).value);
		String id = param.children.get(0).children.get(0).children.get(1).value;
		String type = param.children.get(1).value;
		
		if (actionID.equals("Delay") && id.equals("player")) {
			if (type.equals("INT_LITERAL"))
				return true;
			else if (type.equals("unary -"))
				return true;
			else if (idToType(type).equals("int"))
				return true;
		}
		else if (actionID.equals("Change") && id.equals("player")) {
			if (type.equals("INT_LITERAL"))
				return true;
			else if (type.equals("unary -"))
				return true;
			else if (idToType(type).equals("int"))
				return true;
		} 
		else if (actionID.equals("Direction") && id.equals("player")) {
			if (type.equals("BOOLEAN_LITERAL"))
				return true;
			else if (idToType(type).equals("boolean"))
				return true;
		}
		else if (actionID.equals("GoTo") && id.equals("player")) {
			if (type.equals("cell_expr"))
				return true;
		}
		
		System.out.println("Action: " + actionID + " parameter mismatch");
		
		return false;
	}
	
	String returnTypeName(BGDLTreeNode node, String variable) {
		
		return "";
	}
	
	void addVariableToTable(BGDLTreeNode type, String variable) {
		//System.out.println("Type: " + type.value + " vari: " + variable);
		tt.push(new typeTable(type.value, variable));
	}
	
	// Test current varialbe in Type Table
	void printTypeTable() {
		System.out.println("TT size: " + tt.size());
		for (int i = 0; i < tt.size(); i++) {
			System.out.println("LL: " + tt.get(i).type + ", " + tt.get(i).variable);
		}
	}
	
	String idToType(String id) {
		
		
		for (int i = 0; i < tt.size(); i++) {
			if (tt.get(i).variable.equals(id)) {
				return tt.get(i).type;
			}
		}
		
		return "";
	}
	
	String idToType(BGDLTreeNode node) {
		
		String id = node.value;
//		System.out.println("id: " + id);
		
		if (id.equals(".")) {
			id = node.children.get(1).value;
//			System.out.println("idToType:" + node.children.get(0).value);
//			System.out.println("idToType:" + id);
		}
		if (id.equals("array_expr")) {
//			System.out.println("idToType(array):" + node.children.get(0).children.get(1).value);
			id = node.children.get(0).children.get(1).value;
		}
		
		
		for (int i = 0; i < tt.size(); i++) {
			if (tt.get(i).variable.equals(id)) {
				return tt.get(i).type;
			}
		}
		
		return node.value;
	}
}
